home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / prnt / cups.py < prev    next >
Text File  |  2009-10-09  |  25KB  |  753 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Std Lib
  23. import os
  24. import os.path
  25. import gzip
  26. import re
  27. import time
  28. import urllib
  29. import tempfile
  30. import glob
  31.  
  32. # Local
  33. from base.g import *
  34. from base import utils, models
  35.  
  36. INVALID_PRINTER_NAME_CHARS = """~`!@#$%^&*()=+[]{}()\\/,.<>?'\";:| """
  37.  
  38. # Handle case where cups.py (via device.py) is loaded
  39. # and cupsext doesn't exist yet. This happens in the
  40. # installer and in a fresh sandbox if the Python extensions
  41. # aren't installed yet.
  42. try:
  43.     current_language = os.getenv("LANG")
  44.     newlang = "C"
  45.  
  46.     # this is a workaround due CUPS rejecting all encoding except ASCII
  47.     # and utf-8
  48.     # if the locale contains the encoding, switch to the same locale,
  49.     # but with utf-8 encoding. Otherwise use C locale.
  50.     if current_language is not None and current_language.count('.'):
  51.         newlang, encoding = current_language.split('.')
  52.         newlang += ".UTF-8"
  53.  
  54.     os.environ['LANG'] = newlang
  55.  
  56.     # the same works for LC_CTYPE, in case it's not set
  57.     current_ctype = os.getenv("LC_CTYPE")
  58.     newctype = "C"
  59.  
  60.     if current_ctype is not None and current_ctype.count('.'):
  61.         newctype, encoding = current_ctype.split('.')
  62.         newctype += ".UTF-8"
  63.  
  64.     os.environ['LC_CTYPE'] = newctype
  65.  
  66.     import cupsext
  67.  
  68.     # restore the old env values
  69.     if current_ctype is not None:
  70.         os.environ['LC_CTYPE'] = current_ctype
  71.  
  72.     if current_language is not None:
  73.         os.environ['LANG'] = current_language
  74.  
  75. except ImportError:
  76.     if not os.getenv("HPLIP_BUILD"):
  77.         log.warn("CUPSEXT could not be loaded. Please check HPLIP installation.")
  78.         sys.exit(1)
  79.  
  80.  
  81. IPP_PRINTER_STATE_IDLE = 3
  82. IPP_PRINTER_STATE_PROCESSING = 4
  83. IPP_PRINTER_STATE_STOPPED = 5
  84.  
  85. # Std CUPS option types
  86. PPD_UI_BOOLEAN = 0   # True or False option
  87. PPD_UI_PICKONE = 1   # Pick one from a list
  88. PPD_UI_PICKMANY = 2  # Pick zero or more from a list
  89.  
  90. # Non-std: General
  91. UI_SPINNER = 100           # Simple spinner with opt. suffix (ie, %)
  92. UI_UNITS_SPINNER = 101     # Spinner control w/pts, cm, in, etc. units (not impl.)
  93. UI_BANNER_JOB_SHEETS = 102 # dual combos for banner job-sheets
  94. UI_PAGE_RANGE = 103        # Radio + page range entry field
  95.  
  96. # Non-std: Job storage
  97. UI_JOB_STORAGE_MODE = 104      # Combo w/linkage
  98. UI_JOB_STORAGE_PIN = 105       # Radios w/PIN entry
  99. UI_JOB_STORAGE_USERNAME = 106  # Radios w/text entry
  100. UI_JOB_STORAGE_ID = 107        # Radios w/text entry
  101. UI_JOB_STORAGE_ID_EXISTS = 108 # Combo
  102.  
  103.  
  104. # ipp_op_t
  105. IPP_PAUSE_PRINTER = 0x0010
  106. IPP_RESUME_PRINTER = 0x011
  107. IPP_PURGE_JOBS = 0x012
  108. CUPS_GET_DEFAULT = 0x4001
  109. CUPS_GET_PRINTERS = 0x4002
  110. CUPS_ADD_MODIFY_PRINTER = 0x4003
  111. CUPS_DELETE_PRINTER = 0x4004
  112. CUPS_GET_CLASSES = 0x4005
  113. CUPS_ADD_MODIFY_CLASS = 0x4006
  114. CUPS_DELETE_CLASS = 0x4007
  115. CUPS_ACCEPT_JOBS = 0x4008
  116. CUPS_REJECT_JOBS = 0x4009
  117. CUPS_SET_DEFAULT = 0x400a
  118. CUPS_GET_DEVICES = 0x400b
  119. CUPS_GET_PPDS = 0x400c
  120. CUPS_MOVE_JOB = 0x400d
  121. CUPS_AUTHENTICATE_JOB = 0x400e
  122.  
  123. # ipp_jstate_t
  124. IPP_JOB_PENDING = 3    # Job is waiting to be printed
  125. IPP_JOB_HELD = 4       # Job is held for printing
  126. IPP_JOB_PROCESSING = 5 # Job is currently printing
  127. IPP_JOB_STOPPED = 6    # Job has been stopped
  128. IPP_JOB_CANCELLED = 7  # Job has been cancelled
  129. IPP_JOB_ABORTED = 8    # Job has aborted due to error
  130. IPP_JOB_COMPLETED = 8  # Job has completed successfully
  131.  
  132. # ipp_status_e
  133. IPP_OK = 0x0000 # successful-ok
  134. IPP_OK_SUBST = 0x001 # successful-ok-ignored-or-substituted-attributes
  135. IPP_OK_CONFLICT = 0x002 # successful-ok-conflicting-attributes
  136. IPP_OK_IGNORED_SUBSCRIPTIONS = 0x003 # successful-ok-ignored-subscriptions
  137. IPP_OK_IGNORED_NOTIFICATIONS = 0x004 # successful-ok-ignored-notifications
  138. IPP_OK_TOO_MANY_EVENTS = 0x005 # successful-ok-too-many-events
  139. IPP_OK_BUT_CANCEL_SUBSCRIPTION = 0x006 # successful-ok-but-cancel-subscription
  140. IPP_OK_EVENTS_COMPLETE = 0x007 # successful-ok-events-complete
  141. IPP_REDIRECTION_OTHER_SITE = 0x300
  142. IPP_BAD_REQUEST = 0x0400 # client-error-bad-request
  143. IPP_FORBIDDEN = 0x0401 # client-error-forbidden
  144. IPP_NOT_AUTHENTICATED = 0x0402 # client-error-not-authenticated
  145. IPP_NOT_AUTHORIZED = 0x0403 # client-error-not-authorized
  146. IPP_NOT_POSSIBLE = 0x0404 # client-error-not-possible
  147. IPP_TIMEOUT = 0x0405 # client-error-timeout
  148. IPP_NOT_FOUND = 0x0406 # client-error-not-found
  149. IPP_GONE = 0x0407 # client-error-gone
  150. IPP_REQUEST_ENTITY = 0x0408 # client-error-request-entity-too-large
  151. IPP_REQUEST_VALUE = 0x0409 # client-error-request-value-too-long
  152. IPP_DOCUMENT_FORMAT = 0x040a # client-error-document-format-not-supported
  153. IPP_ATTRIBUTES = 0x040b # client-error-attributes-or-values-not-supported
  154. IPP_URI_SCHEME = 0x040c # client-error-uri-scheme-not-supported
  155. IPP_CHARSET = 0x040d # client-error-charset-not-supported
  156. IPP_CONFLICT = 0x040e # client-error-conflicting-attributes
  157. IPP_COMPRESSION_NOT_SUPPORTED = 0x040f # client-error-compression-not-supported
  158. IPP_COMPRESSION_ERROR = 0x0410 # client-error-compression-error
  159. IPP_DOCUMENT_FORMAT_ERROR = 0x0411 # client-error-document-format-error
  160. IPP_DOCUMENT_ACCESS_ERROR = 0x0412 # client-error-document-access-error
  161. IPP_ATTRIBUTES_NOT_SETTABLE = 0x0413 # client-error-attributes-not-settable
  162. IPP_IGNORED_ALL_SUBSCRIPTIONS = 0x0414 # client-error-ignored-all-subscriptions
  163. IPP_TOO_MANY_SUBSCRIPTIONS = 0x0415 # client-error-too-many-subscriptions
  164. IPP_IGNORED_ALL_NOTIFICATIONS = 0x0416 # client-error-ignored-all-notifications
  165. IPP_PRINT_SUPPORT_FILE_NOT_FOUND = 0x0417 # client-error-print-support-file-not-found
  166. IPP_INTERNAL_ERROR = 0x0500 # server-error-internal-error
  167. IPP_OPERATION_NOT_SUPPORTED = 0x0501 # server-error-operation-not-supported
  168. IPP_SERVICE_UNAVAILABLE = 0x0502 # server-error-service-unavailable
  169. IPP_VERSION_NOT_SUPPORTED = 0x0503 # server-error-version-not-supported
  170. IPP_DEVICE_ERROR = 0x0504 # server-error-device-error
  171. IPP_TEMPORARY_ERROR = 0x0505 # server-error-temporary-error
  172. IPP_NOT_ACCEPTING = 0x0506 # server-error-not-accepting-jobs
  173. IPP_PRINTER_BUSY = 0x0507 # server-error-busy
  174. IPP_ERROR_JOB_CANCELLED = 0x0508 # server-error-job-canceled
  175. IPP_MULTIPLE_JOBS_NOT_SUPPORTED = 0x0509 # server-error-multiple-document-jobs-not-supported
  176. IPP_PRINTER_IS_DEACTIVATED = 0x050a # server-error-printer-is-deactivated
  177.  
  178. CUPS_ERROR_BAD_NAME = 0x0f00
  179. CUPS_ERROR_BAD_PARAMETERS = 0x0f01
  180.  
  181. nickname_pat = re.compile(r'''\*NickName:\s*\"(.*)"''', re.MULTILINE)
  182. pat_cups_error_log = re.compile("""^loglevel\s?(debug|debug2|warn|info|error|none)""", re.I)
  183. ppd_pat = re.compile(r'''.*hp-(.*?)(-.*)*\.ppd.*''', re.I)
  184.  
  185.  
  186.  
  187. def getPPDPath(addtional_paths=None):
  188.     """
  189.         Returns the CUPS ppd path (not the foomatic one under /usr/share/ppd).
  190.         Usually this is /usr/share/cups/model.
  191.     """
  192.     if addtional_paths is None:
  193.         addtional_paths = []
  194.  
  195.     search_paths = prop.ppd_search_path.split(';') + addtional_paths
  196.  
  197.     for path in search_paths:
  198.         ppd_path = os.path.join(path, 'cups/model')
  199.         if os.path.exists(ppd_path):
  200.             return ppd_path
  201.  
  202.  
  203. def getAllowableMIMETypes():
  204.     """
  205.         Scan all /etc/cups/*.convs files for allowable file formats.
  206.     """
  207.     files = glob.glob("/etc/cups/*.convs")
  208.  
  209.     allowable_mime_types = []
  210.  
  211.     for f in files:
  212.         #log.debug( "Capturing allowable MIME types from: %s" % f )
  213.         conv_file = file(f, 'r')
  214.  
  215.         for line in conv_file:
  216.             if not line.startswith("#") and len(line) > 1:
  217.                 try:
  218.                     source, dest, cost, prog =  line.split()
  219.                 except ValueError:
  220.                     continue
  221.  
  222.                 if source not in ('application/octet-stream', 'application/vnd.cups-postscript'):
  223.                     allowable_mime_types.append(source)
  224.  
  225.     # Add some well-known MIME types that may not appear in the .convs files
  226.     allowable_mime_types.append("image/x-bmp")
  227.     allowable_mime_types.append("text/cpp")
  228.     allowable_mime_types.append("application/x-python")
  229.     allowable_mime_types.append("application/hplip-fax")
  230.  
  231.     return allowable_mime_types
  232.  
  233.  
  234. def getPPDDescription(f):
  235.     if f.endswith('.gz'):
  236.         nickname = gzip.GzipFile(f, 'r').read(4096)
  237.     else:
  238.         nickname = file(f, 'r').read(4096)
  239.  
  240.     try:
  241.         desc = nickname_pat.search(nickname).group(1)
  242.     except AttributeError:
  243.         desc = ''
  244.  
  245.     return desc
  246.  
  247.  
  248. def getSystemPPDs():
  249.     major, minor, patch = getVersionTuple()
  250.     ppds = {} # {'ppd name' : 'desc', ...}
  251.  
  252.     if major == 1 and minor < 2:
  253.         ppd_dir = sys_conf.get('dirs', 'ppd')
  254.         log.debug("(CUPS 1.1.x) Searching for PPDs in: %s" % ppd_dir)
  255.  
  256.         for f in utils.walkFiles(ppd_dir, pattern="HP*ppd*;hp*ppd*", abs_paths=True):
  257.             desc = getPPDDescription(f)
  258.  
  259.             if not ('foo2' in desc or
  260.                     'gutenprint' in desc.lower() or
  261.                     'gutenprint' in f):
  262.  
  263.                 ppds[f] = desc
  264.                 log.debug("%s: %s" % (f, desc))
  265.  
  266.     else: # 1.2.x
  267.         log.debug("(CUPS 1.2.x) Getting list of PPDs using CUPS_GET_PPDS...")
  268.         ppd_dict = cupsext.getPPDList()
  269.         cups_ppd_path = getPPDPath() # usually /usr/share/cups/model
  270.         foomatic_ppd_path = sys_conf.get('dirs', 'ppdbase', '/usr/share/ppd')
  271.  
  272.         if not foomatic_ppd_path or not os.path.exists(foomatic_ppd_path):
  273.             foomatic_ppd_path = '/usr/share/ppd'
  274.  
  275.         log.debug("CUPS PPD base path = %s" % cups_ppd_path)
  276.         log.debug("Foomatic PPD base path = %s" % foomatic_ppd_path)
  277.  
  278.         for ppd in ppd_dict:
  279.             if not ppd:
  280.                 continue
  281.  
  282.             if 'hp-' in ppd.lower() or 'hp_' in ppd.lower() and \
  283.                 ppd_dict[ppd]['ppd-make'] == 'HP':
  284.  
  285.                 desc = ppd_dict[ppd]['ppd-make-and-model']
  286.  
  287.                 if not ('foo2' in desc.lower() or
  288.                         'gutenprint' in desc.lower() or
  289.                         'gutenprint' in ppd):
  290.  
  291.                     # PPD files returned by CUPS_GET_PPDS (and by lpinfo -m)
  292.                     # can be relative to /usr/share/ppd/ or to
  293.                     # /usr/share/cups/model/. Not sure why this is.
  294.                     # Here we will try both and see which one it is...
  295.  
  296.                     if os.path.exists(ppd):
  297.                         path = ppd
  298.                     else:
  299.                         try:
  300.                             path = os.path.join(foomatic_ppd_path, ppd)
  301.                         except AttributeError: # happens on some boxes with provider: style ppds (foomatic: etc)
  302.                             path = ppd
  303.                         else:
  304.                             if not os.path.exists(path):
  305.                                 try:
  306.                                     path = os.path.join(cups_ppd_path, ppd)
  307.                                 except AttributeError:
  308.                                     path = ppd
  309.                                 else:
  310.                                     if not os.path.exists(path):
  311.                                         path = ppd # foomatic: or some other driver
  312.  
  313.                     ppds[path] = desc
  314.                     #log.debug("%s: %s" % (path, desc))
  315.  
  316.     return ppds
  317.  
  318.  
  319. ## TODO: Move this to CUPSEXT for better performance
  320. def levenshtein_distance(a,b):
  321.     """
  322.     Calculates the Levenshtein distance between a and b.
  323.     Written by Magnus Lie Hetland.
  324.     """
  325.     n, m = len(a), len(b)
  326.     if n > m:
  327.         a,b = b,a
  328.         n,m = m,n
  329.  
  330.     current = range(n+1)
  331.     for i in range(1,m+1):
  332.         previous, current = current, [i]+[0]*m
  333.  
  334.         for j in range(1,n+1):
  335.             add, delete = previous[j]+1, current[j-1]+1
  336.             change = previous[j-1]
  337.  
  338.             if a[j-1] != b[i-1]:
  339.                 change = change + 1
  340.  
  341.             current[j] = min(add, delete, change)
  342.  
  343.     return current[n]
  344.  
  345.  
  346. number_pat = re.compile(r""".*?(\d+)""", re.IGNORECASE)
  347.  
  348. STRIP_STRINGS2 = ['foomatic:', 'hp-', 'hp_', 'hp ', '.gz', '.ppd',
  349.                   'drv:', '-pcl', '-pcl3', '-jetready',
  350.                  '-zxs', '-zjs', '-ps', '-postscript',
  351.                  '-jr', '-lidl', '-lidil', '-ldl', '-hpijs']
  352.  
  353.  
  354. for p in models.TECH_CLASS_PDLS.values():
  355.     pp = '-%s' % p
  356.     if pp not in STRIP_STRINGS2:
  357.         STRIP_STRINGS2.append(pp)
  358.  
  359.  
  360. STRIP_STRINGS = STRIP_STRINGS2[:]
  361. STRIP_STRINGS.extend(['-series', ' series', '_series'])
  362.  
  363.  
  364. def stripModel2(model): # For new 2.8.10+ PPD find algorithm
  365.     model = model.lower()
  366.  
  367.     for x in STRIP_STRINGS2:
  368.         model = model.replace(x, '')
  369.  
  370.     return model
  371.  
  372.  
  373. def stripModel(model): # for old PPD find algorithm (removes "series" as well)
  374.     model = model.lower()
  375.  
  376.     for x in STRIP_STRINGS:
  377.         model = model.replace(x, '')
  378.  
  379.     return model
  380.  
  381.  
  382. def getPPDFile(stripped_model, ppds): # Old PPD find
  383.     """
  384.         Match up a model name to a PPD from a list of system PPD files.
  385.     """
  386.     log.debug("1st stage edit distance match")
  387.     mins = {}
  388.     eds = {}
  389.     min_edit_distance = sys.maxint
  390.  
  391.     log.debug("Determining edit distance from %s (only showing edit distances < 4)..." % stripped_model)
  392.     for f in ppds:
  393.         t = stripModel(os.path.basename(f))
  394.         eds[f] = levenshtein_distance(stripped_model, t)
  395.         if eds[f] < 4:
  396.             log.debug("dist('%s') = %d" % (t, eds[f]))
  397.         min_edit_distance = min(min_edit_distance, eds[f])
  398.  
  399.     log.debug("Min. dist = %d" % min_edit_distance)
  400.  
  401.     for f in ppds:
  402.         if eds[f] == min_edit_distance:
  403.             for m in mins:
  404.                 if os.path.basename(m) == os.path.basename(f):
  405.                     break # File already in list possibly with different path (Ubuntu, etc)
  406.             else:
  407.                 mins[f] = ppds[f]
  408.  
  409.     log.debug(mins)
  410.  
  411.     if len(mins) > 1: # try pattern matching the model number
  412.         log.debug("2nd stage matching with model number")
  413.  
  414.         try:
  415.             model_number = number_pat.match(stripped_model).group(1)
  416.             model_number = int(model_number)
  417.         except AttributeError:
  418.             pass
  419.         except ValueError:
  420.             pass
  421.         else:
  422.             log.debug("model_number=%d" % model_number)
  423.             matches = {} #[]
  424.             for x in range(3): # 1, 10, 100
  425.                 factor = 10**x
  426.                 log.debug("Factor = %d" % factor)
  427.                 adj_model_number = int(model_number/factor)*factor
  428.                 number_matching, match = 0, ''
  429.  
  430.                 for m in mins:
  431.                     try:
  432.                         mins_model_number = number_pat.match(os.path.basename(m)).group(1)
  433.                         mins_model_number = int(mins_model_number)
  434.                         log.debug("mins_model_number= %d" % mins_model_number)
  435.                     except AttributeError:
  436.                         continue
  437.                     except ValueError:
  438.                         continue
  439.  
  440.                     mins_adj_model_number = int(mins_model_number/factor)*factor
  441.                     log.debug("mins_adj_model_number=%d" % mins_adj_model_number)
  442.                     log.debug("adj_model_number=%d" % adj_model_number)
  443.  
  444.                     if mins_adj_model_number == adj_model_number:
  445.                         log.debug("match")
  446.                         number_matching += 1
  447.                         matches[m] = ppds[m]
  448.                         log.debug(matches)
  449.  
  450.                     log.debug("***")
  451.  
  452.                 if len(matches):
  453.                     mins = matches
  454.                     break
  455.  
  456.     return mins
  457.  
  458.  
  459. def getPPDFile2(stripped_model, ppds): # New PPD find
  460.     # This routine is for the new PPD naming scheme begun in 2.8.10
  461.     # and beginning with implementation in 2.8.12 (Qt4 hp-setup)
  462.     # hp-<model name from models.dat w/o beginning hp_>[-<pdl>][-<pdl>][...].ppd[.gz]
  463.     # 3.9.6: Added handling for hpijs vs. hpcups PPDs/DRVs
  464.     log.debug("Matching PPD list to model %s..." % stripped_model)
  465.     matches = []
  466.     for f in ppds:
  467.         match = ppd_pat.match(f)
  468.         if match is not None:
  469.             if match.group(1) == stripped_model:
  470.                 log.debug("Found match: %s" % f)
  471.                 try:
  472.                     pdls = match.group(2).split('-')
  473.                 except AttributeError:
  474.                     pdls = []
  475.  
  476.                 if (prop.hpcups_build and 'hpijs' not in f) or \
  477.                     ((prop.hpijs_build and 'hpijs' in pdls) or (prop.hpcups_build and 'hpijs' not in pdls)):
  478.                     matches.append((f, [p for p in pdls if p and p != 'hpijs']))
  479.  
  480.     log.debug(matches)
  481.     num_matches = len(matches)
  482.  
  483.     if num_matches == 0:
  484.         log.error("No PPD found for model %s using new algorithm. Trying old algorithm..." % stripped_model)
  485.         matches2 = getPPDFile(stripModel(stripped_model), ppds).items()
  486.         log.debug(matches2)
  487.         num_matches2 = len(matches2)
  488.         if num_matches2:
  489.             for f, d in matches2:
  490.                 match = ppd_pat.match(f)
  491.                 if match is not None:
  492.                     log.debug("Found match: %s" % f)
  493.                     try:
  494.                         pdls = match.group(2).split('-')
  495.                     except AttributeError:
  496.                         pdls = []
  497.  
  498.                     if (prop.hpcups_build and 'hpijs' not in f) and \
  499.                        ((prop.hpijs_build and 'hpijs' in pdls) or (prop.hpcups_build and 'hpijs' not in pdls)):
  500.                         matches.append((f, [p for p in pdls if p and p != 'hpijs']))
  501.  
  502.         log.debug(matches)
  503.         num_matches = len(matches)
  504.  
  505.     if num_matches == 0:
  506.         log.error("No PPD found for model %s using old algorithm." % stripModel(stripped_model))
  507.         return None
  508.  
  509.     elif num_matches == 1:
  510.         log.debug("One match found.")
  511.         return (matches[0][0], '')
  512.  
  513.     # > 1
  514.     log.debug("%d matches found. Selecting based on PDL: Host > PS > PCL/Other" % num_matches)
  515.     for p in [models.PDL_TYPE_HOST, models.PDL_TYPE_PS, models.PDL_TYPE_PCL]:
  516.         for f, pdl_list in matches:
  517.             for x in pdl_list:
  518.                 # default to HOST-based PDLs, as newly supported PDLs will most likely be of this type
  519.                 if models.PDL_TYPES.get(x, models.PDL_TYPE_HOST) == p:
  520.                     log.debug("Selecting '-%s' PPD: %s" % (x, f))
  521.                     return (f, '')
  522.  
  523.     # No specific PDL found, so just return 1st found PPD file
  524.     log.debug("No specific PDL located. Defaulting to first found PPD file.")
  525.     return (matches[0][0], '')
  526.  
  527.  
  528.  
  529. def getErrorLogLevel():
  530.     cups_conf = '/etc/cups/cupsd.conf'
  531.     try:
  532.         f = file(cups_conf, 'r')
  533.     except OSError:
  534.         log.error("%s not found." % cups_conf)
  535.     except IOError:
  536.         log.error("%s: I/O error." % cups_conf)
  537.     else:
  538.         for l in f:
  539.             m = pat_cups_error_log.match(l)
  540.             if m is not None:
  541.                 level = m.group(1).lower()
  542.                 log.debug("CUPS error_log LogLevel: %s" % level)
  543.                 return level
  544.  
  545.     log.debug("CUPS error_log LogLevel: unknown")
  546.     return 'unknown'
  547.  
  548.  
  549. def getPrintJobErrorLog(job_id, max_lines=1000, cont_interval=5):
  550.     ret = []
  551.     s = '[Job %d]' % job_id
  552.     #level = getErrorLogLevel()
  553.     cups_conf = '/var/log/cups/error_log'
  554.  
  555.     #if level in ('debug', 'debug2'):
  556.     if 1:
  557.         try:
  558.             f = file(cups_conf, 'r')
  559.         except (IOError, OSError):
  560.             log.error("Could not open the CUPS error_log file: %s" % cups_conf)
  561.             return ''
  562.  
  563.         else:
  564.             if s in file(cups_conf, 'r').read():
  565.                 queue = utils.Queue()
  566.                 job_found = False
  567.  
  568.                 while True:
  569.                     line = f.readline()
  570.  
  571.                     if s in line:
  572.                         job_found = True
  573.  
  574.                         while len(queue):
  575.                             ret.append(queue.get())
  576.  
  577.                         ret.append(line.strip())
  578.  
  579.                         if len(ret) > max_lines:
  580.                             break
  581.  
  582.                     else:
  583.                         if job_found:
  584.                             queue.put(line.strip())
  585.  
  586.                             if len(queue) > cont_interval:
  587.                                 break
  588.  
  589.             return '\n'.join(ret)
  590.  
  591.  
  592. #
  593. # cupsext wrappers
  594. #
  595.  
  596. def getDefaultPrinter():
  597.     r = cupsext.getDefaultPrinter()
  598.     if r is None:
  599.         log.debug("The CUPS default printer is not set.")
  600.     return r
  601.  
  602. def setDefaultPrinter(printer_name):
  603.     setPasswordPrompt("You do not have permission to set the default printer.")
  604.     return cupsext.setDefaultPrinter(printer_name)
  605.  
  606. def accept(printer_name):
  607.     setPasswordPrompt("You do not have permission to accept jobs on a printer queue.")
  608.     return controlPrinter(printer_name, CUPS_ACCEPT_JOBS)
  609.  
  610. def reject(printer_name):
  611.     setPasswordPrompt("You do not have permission to reject jobs on a printer queue.")
  612.     return controlPrinter(printer_name, CUPS_REJECT_JOBS)
  613.  
  614. def start(printer_name):
  615.     setPasswordPrompt("You do not have permission to start a printer queue.")
  616.     return controlPrinter(printer_name, IPP_RESUME_PRINTER)
  617.  
  618. def stop(printer_name):
  619.     setPasswordPrompt("You do not have permission to stop a printer queue.")
  620.     return controlPrinter(printer_name, IPP_PAUSE_PRINTER)
  621.  
  622. def purge(printer_name):
  623.     setPasswordPrompt("You do not have permission to purge jobs.")
  624.     return controlPrinter(printer_name, IPP_PURGE_JOBS)
  625.  
  626. def controlPrinter(printer_name, cups_op):
  627.     if cups_op in (CUPS_ACCEPT_JOBS, CUPS_REJECT_JOBS, IPP_PAUSE_PRINTER, IPP_RESUME_PRINTER, IPP_PURGE_JOBS):
  628.         return cupsext.controlPrinter(printer_name, cups_op)
  629.  
  630.     return 0;
  631.  
  632. def openPPD(printer):
  633.     if not printer:
  634.         return
  635.  
  636.     return cupsext.openPPD(printer)
  637.  
  638. def closePPD():
  639.     return cupsext.closePPD()
  640.  
  641. def getPPD(printer):
  642.     if not printer:
  643.         return
  644.  
  645.     return cupsext.getPPD(printer)
  646.  
  647. def getPPDOption(option):
  648.     return cupsext.getPPDOption(option)
  649.  
  650. def getPPDPageSize():
  651.     return cupsext.getPPDPageSize()
  652.  
  653. def getPrinters():
  654. ##    p2 = []
  655. ##    p = cupsext.getPrinters()
  656. ##    for pp in p:
  657. ##        print pp
  658. ##        try:
  659. ##            pn = pp.name.decode('utf-8')
  660. ##        except UnicodeError:
  661. ##            pass
  662. ##
  663. ##        p2.append(pp)
  664. ##
  665. ##    return p2
  666.     return cupsext.getPrinters()
  667.  
  668. def getJobs(my_job=0, completed=0):
  669.     return cupsext.getJobs(my_job, completed)
  670.  
  671. def getAllJobs(my_job=0):
  672.     return cupsext.getJobs(my_job, 0) + cupsext.getJobs(my_job, 1)
  673.  
  674. def getVersion():
  675.     return cupsext.getVersion()
  676.  
  677. def getVersionTuple():
  678.     return cupsext.getVersionTuple()
  679.  
  680. def getServer():
  681.     return cupsext.getServer()
  682.  
  683. def cancelJob(jobid, dest=None):
  684.     setPasswordPrompt("You do not have permission to cancel a job.")
  685.     if dest is not None:
  686.         return cupsext.cancelJob(dest, jobid)
  687.     else:
  688.         jobs = cupsext.getJobs(0, 0)
  689.         for j in jobs:
  690.             if j.id == jobid:
  691.                 return cupsext.cancelJob(j.dest, jobid)
  692.  
  693.     return False
  694.  
  695. def resetOptions():
  696.     return cupsext.resetOptions()
  697.  
  698. def addOption(option):
  699.     return cupsext.addOption(option)
  700.  
  701. def getOptions():
  702.     return cupsext.getOptions()
  703.  
  704. def printFile(printer, filename, title):
  705.     if os.path.exists(filename):
  706.         return cupsext.printFileWithOptions(printer, filename, title)
  707.     else:
  708.         return -1
  709.  
  710. def addPrinter(printer_name, device_uri, location, ppd_file, model, info):
  711.     log.debug("addPrinter('%s', '%s', '%s', '%s', '%s', '%s')" %
  712.         ( printer_name, device_uri, location, ppd_file, model, info))
  713.  
  714.     if ppd_file and not os.path.exists(ppd_file):
  715.         log.error("PPD file '%s' not found." % ppd_file)
  716.         return (-1, "PPD file not found")
  717.  
  718.     return cupsext.addPrinter(printer_name, device_uri, location, ppd_file, model, info)
  719.  
  720. def delPrinter(printer_name):
  721.     setPasswordPrompt("You do not have permission to delete a printer.")
  722.     return cupsext.delPrinter(printer_name)
  723.  
  724. def getGroupList():
  725.     return cupsext.getGroupList()
  726.  
  727. def getGroup(group):
  728.     return cupsext.getGroup(group)
  729.  
  730. def getOptionList(group):
  731.     return cupsext.getOptionList(group)
  732.  
  733. def getOption(group, option):
  734.     return cupsext.getOption(group, option)
  735.  
  736. def getChoiceList(group, option):
  737.     return cupsext.getChoiceList(group, option)
  738.  
  739. def getChoice(group, option, choice):
  740.     return cupsext.getChoice(group, option, choice)
  741.  
  742. def setOptions():
  743.     return cupsext.setOptions()
  744.  
  745. def removeOption(option):
  746.     return cupsext.removeOption(option)
  747.  
  748. def setPasswordCallback(func):
  749.     return cupsext.setPasswordCallback(func)
  750.  
  751. def setPasswordPrompt(prompt):
  752.     return cupsext.setPasswordPrompt(prompt)
  753.